﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

using BLToolkit.Data.DataProvider;
using BLToolkit.DataAccess;
using FirebirdSql.Data.FirebirdClient;

namespace Rsdn.Janus.Firebird
{
    [JanusDBDriver(
        DriverName,
        "Rsdn.Janus.Firebird.Resources",
        "DriverDisplayName",
        "DriverDescription")]
    internal class FBDriver : IDBDriver
    {
        public const string DriverName = "Fb";

        private static readonly Regex _wrapDbObjectNamesRx = new Regex(
            @"([^\[]*(?<object>\[[^\]]*\])[^\[]*)*",
            RegexOptions.IgnoreCase);

        private readonly FbSqlFormatter _sqlFormatter = new FbSqlFormatter();

        public FBDriver(IServiceProvider provider)
        {
        }

        #region IDBDriver Members
        public bool CheckConnectionString(string constr)
        {
            try
            {
                FbConnectionStringBuilder csbCheck = new FbConnectionStringBuilder(constr) { Pooling = false };

                using (FbConnection con = new FbConnection(csbCheck.ConnectionString))
                {
                    con.Open();
                }
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Проверить если файл базы данных существует.
        /// Ошибки, посредством исключений
        /// </summary>
        /// <param name="constr">Строка подключения</param>
        /// <exception cref="DataAccessException">Файл базы данных {0} не найден</exception>
        public void CheckDbPresence(string constr)
        {
            FbConnectionStringBuilder csbCheck = new FbConnectionStringBuilder(constr);
            if (!File.Exists(csbCheck.DataSource))
            {
                throw new DataAccessException(string.Format("Файл базы данных {0} не найден", csbCheck.DataSource));
            }
        }

        /// <summary>
        /// Получить драйвер схемы.
        /// </summary>
        /// <returns></returns>
        public IDBSchemaDriver CreateSchemaDriver()
        {
            return new FBSchemaDriver();
        }

        public DbConnectionStringBuilder CreateConnectionString()
        {
            return new FbConnectionStringBuilder();
        }

        public DbConnectionStringBuilder CreateConnectionString(string constr)
        {
            return new FbConnectionStringBuilder(constr);
        }

        public IDBConfigControl CreateConfigControl()
        {
            return new FbConfigControl();
        }

        /// <summary>
        /// Создать провайдер для BLToolkit.
        /// </summary>
        public DataProviderBase CreateDataProvider()
        {
            return new FdpDataProvider();
        }

        /// <summary>
        /// Обработать запрос перед выполнением.
        /// </summary>
        public string PreprocessQueryText(string text)
        {
            return WrapDbObjectNames(text);
        }

        /// <summary>
        /// Ссылка на форматтер SQL.
        /// </summary>
        public ISqlFormatter Formatter
        {
            get { return _sqlFormatter; }
        }
        #endregion

        /// <summary>
        /// Оборачивает имена объектов указаных как [object]
        /// в специфичный для движка identifier delimiter
        /// </summary>
        /// <param name="sql">Инструкция для обработки</param>
        private static string WrapDbObjectNames(string sql)
        {
            var match = _wrapDbObjectNamesRx.Match(sql);

            if (match.Success)
            {
                var replacements =
                    new Dictionary<string, string>();

                foreach (Capture capture in match.Groups["object"].Captures)
                    if (!replacements.ContainsKey(capture.Value))
                        replacements.Add(
                            capture.Value,
                            capture.Value.Replace(@"[", @"""").Replace(@"]", @""""));

                sql = replacements.Aggregate(sql, (current, kvp) => current.Replace(kvp.Key, kvp.Value));
            }

            return sql;
        }
    }
}